---
The builtin libs for Tea

@created 2019/02 by Benny Lin
@copyright YJ Technology Ltd. All rights reserved.
---

public NL = "\n"

#php isset(expr Any) Bool
#php empty(expr Any) Bool
#php var_dump(expr0 Any, expr1 Any = #default, expr2 Any = #default, expr3 Any = #default)
#php print_r(expr, is_return Bool = #default)

// php builtin constants
#php __DIR__ String
#php __FILE__ String
#php __LINE__ UInt
#php __CLASS__ UInt
#php __FUNCTION__ UInt

// php super variables
#php $_GET Dict
#php $_POST Dict
#php $_COOKIE Dict
#php $_SERVER Dict

// exception
#php Exception {
	public construct(message String, code Int = 0)
	public getCode() Int
	public getMessage() String
}
#php ErrorException: Exception {}

// file
#php dirname(path String, levels UInt = #default) String
#php basename(path String, suffix String = #default) String
#php realpath(path String) String
#php file_get_contents(file String) String
#php file_set_contents(file String, data String, flags Int = #default) UInt
#php FILE_APPEND Int
#php LOCK_EX Int

// type
#php strval(val) String
#php intval(val) Int
#php floatval(val) Float
#php boolval(val) Bool

public uintval(val) UInt {
	return val as UInt
}

public uint_ensure(num Int) {
	if num < 0 {
		throw ErrorException('Cannot use $num as a UInt value.')
	}

	return num
}

#php is_int(val) Bool
public is_uint(val) Bool {
	return is_int(val) and val >= 0
}

// math
#php abs(val) Float
#php round(val, precision = 0) Float
#php ceil(val) Float
#php floor(val) Float

// string
#php trim(str, char_mask String = #default) String
#php rtrim(str, char_mask String = #default) String
#php ltrim(str, char_mask String = #default) String

// with default charset
#php iconv_strlen(str String) UInt
#php iconv_substr(str String, start Int, length UInt = #default) String

// first pos in default charset
#php iconv_strpos(str String, search String, offset Int = 0) Int  // would return false on not found
public _iconv_strpos(str String, search String, offset Int = 0) Int {
	pos = iconv_strpos(str, search, offset);
	return pos === false ? -1 : pos  // return -1 on not found
}

// last pos in default charset
#php iconv_strrpos(str String, search String, offset Int = 0) Int  // would return false on not found
public _iconv_strrpos(str String, search String, offset Int = 0) Int {
	pos = iconv_strrpos(str, search, offset);
	return pos === false ? -1 : pos  // return -1 on not found
}

#php mb_strtoupper(str String) String
#php mb_strtolower(str String) String

// bytes mode functions
#php strlen(str String) UInt
#php substr(str String, start Int, length UInt = #default) String
#php strpos(master String, search String, offset Int = 0) Int  // would return false on not found
public _strpos(master String, search String, offset Int = 0) Int {
	pos = strpos(master, search, offset);
	return pos === false ? -1 : pos  // return -1 on not found
}
#php strrpos(master String, search String, offset Int = 0) Int  // would return false on not found
public _strrpos(master String, search String, offset Int = 0) Int {
	pos = strrpos(master, search, offset);
	return pos === false ? -1 : pos  // would return -1 on not found
}
#php strtoupper(str String) String
#php strtolower(str String) String

// replace in binary mode
#php strtr(master String, changes Dict) String
#php str_replace(search, replacement, master String) String
public _str_replace(master String, search String, replacement String) {
	return str_replace(search, replacement, master);
}

#php implode(glue String, pieces) String
#php explode(delimiter String, master String) Array

// array
#php count(array) UInt
#php in_array(needle, haystack, strict Bool = false) Bool
#php array_reverse(array)
#php array_map(callback Callable, items Array) Array
#php array_filter(items Array, callback Callable, flag Int = #default) Array
#php array_reduce(items Array, callback Callable, initial Any = #default)

// index array
#php array_slice(master Array, offset Int, length UInt = none) Array
#php array_unshift(master Array, item Any) UInt
#php array_shift(master Array) Any
#php array_push(master Array, item Any) UInt
#php array_pop(master Array) Any
#php array_search(search, master) Int
public _array_search(master Array, search) Int {
	index = array_search(search, master)
	return index === false ? -1 : index  // would return -1 on not found
}
public _dict_search(master Dict, search) String {
	key = array_search(search, master)
	return key === false ? none : key  // would return none on not found
}
#php array_key_exists(key, arr Dict) Bool
public array_last_index(array Array) UInt {
	return count(array) - 1
}

// assoc array
#php unset(target) Void
#php array_keys(array Dict) Array
#php array_values(array Dict) Array

#php JSON_UNESCAPED_UNICODE Int
#php json_encode(data, flags Int = 0, depth UInt = 512) String
#php json_decode(data, assoc Bool = false, depth UInt = 512, flags Int = 0) Dict
#php date(format String, timestamp Int = #default) String

// regex
#php PREG_SPLIT_DELIM_CAPTURE Int
#php preg_match(regex Regex, subject String, &matches Array = #default, flags Int = #default, offset Int = #default) Bool
#php preg_match_all(regex Regex, subject String, &matches Array = #default, flags Int = #default, offset Int = #default) Bool
#php preg_split(regex Regex , subject String, limit Int = #default, flags Int = #default) Array
public regex_test(regex Regex, subject String) Bool {
	return preg_match(regex, subject) ? true : false
}
public regex_match(regex Regex, subject String) String.Array {
	var matches String.Array
	count = preg_match(regex, subject, matches)
	return count === 0 ? none : matches
}
public regex_matches(regex Regex, subject String) String.Array.Array {
	var matches String.Array.Array
	count = preg_match_all(regex, subject, matches)
	return count === 0 ? none : matches
}

// 所有 masked function 在定义时都需要考虑参数的顺序，参数的运行时序不一致可能会导致问题
// 考虑禁止在调用函数的参数中改变数据（需要推断出可能改变数据的函数，或直接禁止参数带函数调用），纯表达式?
// 部分实现中，参数编译后顺序有变化，使用时应避免当前字符串被delimiter表达式所改变

// public IShareAble {
// 	vshare() Object
// }

// public ICloneAble {
// 	vclone() Object
// }

internal IBaseType {
	masked string() => strval(this)
	masked int() => intval(this)
	masked uint() => uintval(this)
	masked float() => floatval(this)
}

#tea MetaType {}
#tea None {}
#tea Void {}
#tea Any {}

#tea String: IBaseType {
	// process in dist native mode
	masked length UInt => strlen(this)
	masked find(str String, offset Int = 0) => _strpos(this, str, offset)
	masked find_last(str String, offset Int = 0) => _strrpos(this, str, offset)
	masked copy(start Int, length UInt = #default) => substr(this, start, length)
	masked lower_case() => strtolower(this)
	masked upper_case() => strtoupper(this)

	// process in byte mode
	masked byte_length UInt => strlen(this)
	masked byte_find(str String, offset Int = 0) => _strpos(this, str, offset)
	masked byte_find_last(str String, offset Int = 0) => _strrpos(this, str, offset)
	masked byte_copy(start Int, length UInt = #default) => substr(this, start, length)
	masked byte_lower() => strtolower(this)
	masked byte_upper() => strtoupper(this)

	// process in rune mode
	masked rune_length UInt => iconv_strlen(this)
	masked rune_find(str String, offset Int = 0) => _iconv_strpos(this, str, offset)
	masked rune_find_last(str String, offset Int = 0) => _iconv_strrpos(this, str, offset)
	masked rune_copy(start Int, length UInt = #default) => iconv_substr(this, start, length)
	masked rune_lower() => mb_strtolower(this)
	masked rune_upper() => mb_strtoupper(this)

	// other
	masked explode(delimiter String) => explode(delimiter, this)
	masked replace(search String, replacement String) => _str_replace(this, search, replacement)
	masked json_decode() => json_decode(this, true)
}

#tea Float: IBaseType {
	masked abs() => abs(this)
	masked ceil() => ceil(this)
	masked floor() => floor(this)
	masked round(precision = 0) => round(this, precision)
}

#tea Int: IBaseType {
	masked uint() UInt => abs(this)
	masked abs() UInt => abs(this)
}

#tea UInt: IBaseType {}

#tea Bool {
	masked int() Int => intval(this)
	masked uint() UInt => intval(this)
}

#tea Array {
	masked length UInt => count(this)
	masked end UInt => array_last_index(this)

	masked has_value(val) => in_array(val, this)
	masked find(val) => _array_search(this, val) // find the index with supported value
	masked copy(start Int, length UInt = #default) Array => array_slice(this, start, length)
	masked reverse() Array => array_reverse(this)

	masked unshift(item) => array_unshift(this, item)
	masked shift() => array_shift(this)
	masked push(item) => array_push(this, item)
	masked pop() => array_pop(this)

	// callback(item)
	masked map(callback Callable) => array_map(callback, this)

	// callback(carry, item)
	masked reduce(callback Callable, initial Any = #default) => array_reduce(this, callback, initial)

	// callback(item) Bool
	masked filter(callback Callable) => array_filter(this, callback)

	masked join(glue String = NL) => implode(glue, this)
	masked json_encode(flags Int = JSON_UNESCAPED_UNICODE) => json_encode(this, flags)
}

#tea Dict {
	masked length => count(this)

	masked keys() => array_keys(this)
	masked values() => array_values(this)

	masked has_key(key) => array_key_exists(key, this)
	masked has_value(val) => in_array(val, this)
	masked find(val) => _dict_search(this, val) // find the key with supported value

	// masked remove(key String) => unset(this[key])

	masked join(glue String = NL) => implode(glue, this)
	masked json_encode(flags Int = JSON_UNESCAPED_UNICODE) => json_encode(this, flags)
}

#tea Regex {
	masked test(text String) Bool => regex_test(this, text)

	// match, returns the first matched item
	masked match(text String) String.Array => regex_match(this, text)

	// match, returns the all matched items
	masked matches(text String) String.Array.Array => regex_matches(this, text)

	// split text, returns the splited items
	masked split(text String) String.Array => preg_split(this, text)

	// split text, returns include splited items and separators
	masked separate(text String) String.Array => preg_split(this, text, -1, PREG_SPLIT_DELIM_CAPTURE)
}

#tea XView {}   // accept IView
public IView {}

#tea Callable {}

#tea Iterable {} // accept IteratorInterface
#php Iterator as IteratorInterface {
	current() Any
	key() String
	next()
	rewind()
	valid() Bool
}

#php Generator as GeneratorInterface: IteratorInterface {
	send(value Any) Any
	throw(exception Exception)
}

// end of program
